package io.anywhere

import java.io.File

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.Uri.Path
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Route
import akka.stream.ActorMaterializer
import com.beust.jcommander.JCommander
import com.typesafe.config.ConfigFactory

import scala.collection.concurrent.TrieMap
import scala.io.Source

object Boot {

  val cache_content = TrieMap[Path, (Long, Array[Byte])]()

  def data(file: File, path: Path, option: HttpOption): Array[Byte] = {
    if (option.cache) {
      if (!cache_content.contains(path) || cache_content(path)._1 != file.lastModified()) {
        cache_content.put(path, (file.lastModified(), read(file)))
      }
      cache_content(path)._2
    }
    else read(file)
  }

  def read(file: File): Array[Byte] = {
    val source = Source.fromFile(file)
    try source.mkString.getBytes finally source.close()
  }

  def route(option: HttpOption): Route = { ctx =>
    val path = if (ctx.request.uri.path == Uri.Path("/")) Uri.Path("/index.html") else ctx.request.uri.path
    val file = new File(System.getProperty("user.dir") + path.toString())
    if (ctx.request.method == HttpMethods.GET && file.exists())
      ctx.complete {
        val contentType = file.getName.substring(file.getName.lastIndexOf(".")) match {
          case ".htm" | ".html" => MediaTypes.`text/html` withCharset HttpCharsets.`UTF-8`
          case ".jpg" | ".jpeg" => MediaTypes.`image/jpeg`.toContentType
          case ".png" => MediaTypes.`image/png`.toContentType
          case ".gif" => MediaTypes.`image/png`.toContentType
          case ".css" => MediaTypes.`text/css` withCharset HttpCharsets.`UTF-8`
          case _ => MediaTypes.`text/plain` withCharset HttpCharsets.`UTF-8`
        }
        HttpResponse(entity = HttpEntity(contentType, data(file, path, option)))
      }
    else
      ctx.complete {
        HttpResponse(status = StatusCodes.NotFound, entity = HttpEntity(ContentTypes.`text/html(UTF-8)`, s"${ctx.request.uri.path} Not found."))
      }
  }

  def main(args: Array[String]) {
    implicit val system = ActorSystem("my-system", ConfigFactory.parseResources(this.getClass.getClassLoader, "application.conf"))
    implicit val materializer = ActorMaterializer()
    implicit val ex = system.dispatcher

    val option = new HttpOption
    new JCommander(option, args: _*)

    Http().bindAndHandle(route(option), option.host, option.port)

    println(s"Server online at http://${option.host}:${option.port}.")
  }
}
